﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
/// <summary>
/// 此文档包含了各种运控配置
/// 轴参数,IO参数,坐标系配置,凸轮配置。
/// </summary>
namespace IndustryWrapper
{
    /// <summary>
    /// 根据控制卡的配置文件生成IO列表和轴列表
    /// </summary>
    public class CMotionConfigeBase
    {
        /// <summary>
        /// 导出轴配置到csv中,方便用户编辑
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public virtual bool exportAxisToCsv(string csv)
        {
            return true;
        }
        /// <summary>
        /// 导出IO配置到csv中,方便用户编辑
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public virtual bool exportIoToCsv(string csv)
        {
            return true;
        }
        public virtual bool importFromCsv(string csv)
        {
            return true;
        }
        public virtual bool importFromXml(string xml)
        {
            return true;
        }
    }
    /// <summary>
    /// 基于xml的硬件配置文件
    /// 不同的控制卡控制器采用不同的方式
    /// 在文件中指明控制卡类型
    /// 然后调用不同的控制卡初始化函数
    /// </summary>
    public class CMotionHardConfige
    {
        //控制卡的类型
        public ECardType cardType = ECardType.GTS;
        //配置文件,可能是所有控制卡控制器的类型
        public CMotionConfigeBase cfg = null;
        public bool freshSelf = false;//自主刷新
        public int freshTime = 5;//刷新时间 MS
        public int netinittype = 99;//网络初始化号
        public CMotionHardConfige()
        {
            //if (!readfromxml())
            //    System.Windows.Forms.MessageBox.Show("读取硬件配置失败!");
        }
        /// <summary>
        /// 通过控制卡类型名字来设置卡类型
        /// </summary>
        /// <param name="typeStr"></param>
        /// <returns></returns>
        private bool setTypeByName(string typeStr)
        {
            string tmpTypeStr = typeStr.ToUpper();
            if (tmpTypeStr.IndexOf("GTS") >= 0)//TODO:添加凌华,正运动,雷赛等平拍
            {
                cardType = ECardType.GTS;
                return true;
            }
            else if (tmpTypeStr.IndexOf("GEN") >= 0)
            {
                cardType = ECardType.GEN;
                return true;
            }
            else if (tmpTypeStr.IndexOf("GSN") >= 0)
            {
                cardType = ECardType.GSN;
                return true;
            }
            else if (tmpTypeStr.IndexOf("GNS") >= 0)
            {
                cardType = ECardType.GSN;
                return true;
            }
            else if (tmpTypeStr.IndexOf("GUC") >= 0)
            {
                cardType = ECardType.GUC;
                return true;
            }
            else if (tmpTypeStr.IndexOf("GVN") >= 0)
            {
                cardType = ECardType.GVN;
                return true;
            }
            return false;
        }
        /// <summary>
        /// 从xml中读取配置文件
        /// cfg会被新建和赋值
        /// 先读取卡的类型,再根据类型创建不同的卡或者控制器的构造函数
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public bool readfromxml(string xml = @"confige\hard.xml")
        {
            try
            {
                //先读取类型
                System.Xml.XmlDocument doc = new XmlDocument();
                doc.Load(xml);
                XmlNode typeNd = doc.SelectSingleNode("/Hard/Type");
                if (typeNd == null)
                    return false;
                typeNd = doc.SelectSingleNode("/Hard/FreshSelf");
                if (typeNd != null)
                    freshSelf = bool.Parse(typeNd.InnerText);
                typeNd = doc.SelectSingleNode("/Hard/netinittype");
                if (typeNd != null)
                    netinittype = int.Parse(typeNd.InnerText);
                typeNd = doc.SelectSingleNode("/Hard/FreshSelfTime");
                if (typeNd != null)
                    freshTime = int.Parse(typeNd.InnerText);
                typeNd = doc.SelectSingleNode("/Hard/Type");
                string typeStr = typeNd.InnerText.ToUpper();
                if (!setTypeByName(typeStr))
                    return false;
                //创建配置数据
                switch (cardType)
                {
                    case ECardType.GTS:
                        cfg = new CCfgGts();
                        return cfg.importFromXml(xml);
                    case ECardType.GSN:
                        cfg = new CCfgGsn();
                        return cfg.importFromXml(xml);
                    case ECardType.GEN:
                        break;
                    case ECardType.GAOCHUAN:
                        break;
                    case ECardType.LEISAI:
                        break;
                    case ECardType.LINGHUA:
                        break;
                    case ECardType.GUC:
                        break;
                    case ECardType.GNS:
                        break;
                    case ECardType.GVN:
                        cfg = new CCfgGvn();
                        return cfg.importFromXml(xml);
                    default:
                        break;
                }   
                return false;
            }
            catch (Exception e)
            {
                System.Windows.Forms.MessageBox.Show("打开文件错误" + e.ToString());
                return false;
            }
        }
        /// <summary>
        /// 通过配置导出轴列表
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public bool exportAxisToCsv(string csv = @"axis.csv")
        {
            if (cfg != null)
            {
                return cfg.exportAxisToCsv(csv);
            }
            return false;
        }
        /// <summary>
        /// 通过配置导出IO列表
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public bool exportIoToCsv(string csv = @"io.csv")
        {
            if (cfg != null)
            {
                return cfg.exportIoToCsv(csv);
            }
            return false;
        }
    }
    /// <summary>
    /// 轴配置类
    /// 1 单轴的配置文件
    /// 2 按轴模块来配置的文件
    /// 3 序列化使用外部序列化类
    /// 4 支持csv导入,导入的时候自动分析csv中的表头来查找属性自动设置,csv中可以选择需要设置的参数几个参数即可
    /// </summary>
    [Serializable]
    public class CAxisConfige
    {
        public static string defaultfn = @"confige/axis.xml";
        /// <summary>
        /// 按模块来划分,便于整体读取,节省读取时间
        /// </summary>
        public List<CAxisModelPrm> model = new List<CAxisModelPrm>();
        /// <summary>
        /// 原始的轴参数数据
        /// </summary>
        public List<CAxisSinglePrm> data = new List<CAxisSinglePrm>(); 
        public CAxisConfige()
        {
            //if(readfromcsv_axis())
            //    CDebug.log.add("读取轴配置成功.");
            //else
            //    CDebug.log.add("读取轴配置文件失败!",ELogType.ERROR);
        }
        /// <summary>
        /// 从csv格式的文件中读取配置
        /// 格式要求:
        /// 第一行:字段的名称,和属性名称一致
        /// 第二行:字段备注,可以是空
        /// 后面行:部分参数数据
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public bool readfromcsv(string csv=@"confige/axis.csv")
        {
            if (csv == "")
                return false;
            bool dataErr = false;
            try
            {
                Encoding _encode = FileEncoding.GetType(csv); ;// Encoding.GetEncoding("GB2312");
                StreamReader sr = new StreamReader(csv, _encode);
                if (sr == null)
                    return false;
                //清空数据
                data.Clear();
                //读取所有数据文件
                List<string> lines = new List<string>();
                while (!sr.EndOfStream)
                {
                    string str = sr.ReadLine();
                    lines.Add(str);
                }
                sr.Close();
                //前面两行是注释,不能没有轴
                if (lines.Count < 3)
                    return false;
                //提前创建数据
                int axisCount = lines.Count - 2;
                for (int i = 0; i < axisCount; i++)
                {
                    data.Add(new CAxisSinglePrm());
                }
                //第1行是关键字解析
                string[] keyWords = lines[0].Split(',');
                if (keyWords.Length <=0)
                    return false;
                //全部转换成大写,方便不区分大小写
                for (int i = 0; i < keyWords.Length; i++)
                {
                    keyWords[i] = keyWords[i].ToUpper();
                    //不能有空字符,空得都要返回错误
                    if (keyWords[i] == "")
                        return false;
                }
                //遍历所有行
                CAxisSinglePrm p = new CAxisSinglePrm();//临时用 没啥意义
                for (int J = 0; J < keyWords.Length; J++)
                {
                    PropertyInfo[] propertyInfos = p.GetType().GetProperties();
                    foreach (var item in propertyInfos)
                    {
                        if (item.Name.ToUpper() == keyWords[J])
                        {
                            if (item.Name.ToUpper() == "HOMETRIGER")
                            {

                            }
                            for (int i = 0; i < axisCount; i++)
                            {
                                string[] line = lines[i + 2].Split(',');
                                string typeStr = item.PropertyType.ToString();
                                //TODO:如果轴参数类中有别的数据类型需要这里添加
                                typeStr = typeStr.Replace("System.", "");
                                switch (typeStr)
                                {
                                    case "Double":
                                        double value1 = double.Parse(line[J]);
                                        item.SetValue(data[i], value1);
                                        break;
                                    case "Int32":
                                        int value2  = int.Parse(line[J]);
                                        item.SetValue(data[i], value2);
                                        break;
                                    case "String":
                                        System.String value3 = line[J];
                                        item.SetValue(data[i], value3);
                                        break;
                                    case "Short":
                                    case "Int16":
                                        short value4 = short.Parse(line[J]);
                                        item.SetValue(data[i], value4);
                                        break;
                                    case "Int64":
                                        long value6 = long.Parse(line[J]);
                                        item.SetValue(data[i], value6);
                                        break;
                                    case "Single":
                                        float value7 = float.Parse(line[J]);
                                        item.SetValue(data[i], value7);
                                        break;
                                    default:
                                        if (!dataErr)
                                        {
                                            System.Windows.Forms.MessageBox.Show("发现轴参数中未知数据类型!");
                                            dataErr = true;
                                        }
                                        break;
                                }
                            }
                            break;
                        }
                    }
                }
                return true;
            }
            catch (Exception)
            {
                data.Clear();
                System.Windows.Forms.MessageBox.Show("轴参数文档打开失败!");
                return false;
            }
        }
        /// <summary>
        /// 把所有的数据都导出到csv中
        /// 按照上面个格式导出
        /// 只是导出单个轴的配置
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public bool export2csv(string csv = @"confige/axis.csv")
        {
            try
            {
                //检查是否文件存在在
                if (!File.Exists(csv))
                    File.Create(csv).Close();
                StreamWriter sw = new StreamWriter(csv, true, Encoding.UTF8);
                List<string> lines = new List<string>();
                List<string> columns = new List<string>();
                var p = new CAxisSinglePrm();
                //制作表头
                PropertyInfo[] propertyInfos = p.GetType().GetProperties();
                string title = "";
                for (int i = 0; i < propertyInfos.Length; i++)
                {
                    columns.Add(propertyInfos[i].Name);
                    if (i < propertyInfos.Length - 1)
                        title += propertyInfos[i].Name + ",";
                    else 
                        title += propertyInfos[i].Name;
                }
                lines.Add(title);
                //保存备注
                string note = title;
                lines.Add(note);
                //TODO:需要导出数据,保存数据
                for (int i = 0; i < data.Count; i++)
                {

                }
                sw.Flush();
                sw.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
    /// <summary>
    /// 整个轴模块来划分 
    /// 方便统一的用一系列指令来读取整个数据
    /// </summary>
    [Serializable]
    public class CAxisModelPrm
    {
        public List<CAxisSinglePrm > data = new List<CAxisSinglePrm>();
        //固高卡特有
        //对于GSN来的轴模块来说ZZZZZZZZ_XXXXXXXX_YYYY
        //XXXXXX是数据区域的dataStartIndex
        //YYYY是卡号
        //ZZZZZZZZ是轴的startIndex 
        private long modelSign = 0;
        //状态和规划位置  实际位置是必须读取的,剩余的按位来设置是否读取  减少CPI通讯
        //0位开始 bit0=规划速度 bit1=实际速度 bit2= 硬件homelmtp lmtn
        private static long readSign = 0b111;//指定哪些数据需要读取
        public CAxisModelPrm()
        {

        }
        public void Add(CAxisSinglePrm d)
        {
            if (d != null)
                data.Add(d);
        }
        public void Clear()
        {
            modelSign = 0;
            data.Clear();
        }
        #region 属性
        /// <summary>
        /// 可以读取规划速度
        /// </summary>
        public static bool ReadPrfVel
        {
            get
            {
                return (readSign & 0b1) != 0;
            }
            set
            {
                if (value)
                {
                    readSign = ((readSign & 0b110) + 1);
                }
                else
                {
                    readSign = (readSign & 0b110);
                }
            }
        }
        /// <summary>
        /// 可以读取编码器速度
        /// </summary>
        public static bool ReadEncVel
        {
            get
            {
                return (readSign & 0b10) != 0;
            }
            set
            {
                if (value)
                {
                    readSign = ((readSign & 0b101) + 2);
                }
                else
                {
                    readSign = (readSign & 0b101);
                }
            }
        }
        /// <summary>
        /// 读取轴传感器
        /// </summary>
        public static bool ReadSW
        {
            get
            {
                return (readSign & 0b100) != 0;
            }
            set
            {
                if (value)
                {
                    readSign = ((readSign & 0b011) + 4);
                }
                else
                {
                    readSign = (readSign & 0b11);
                }
            }
        }
        /// <summary>
        /// 轴数量
        /// </summary>
        public int AxisCount
        {
            get
            {
                return data.Count;
            }
        }
        /// <summary>
        /// 轴的起始号 从0开始的
        /// </summary>
        public int StartIndex
        {
            get
            {
                return (int)((modelSign & 0xFF000) >> 12);
            }
            set
            {
                modelSign = (modelSign & 0xFFF) + (value << 12);
            }
        }
        /// <summary>
        /// 轴数据放在列表中的哪个位置
        /// 从0开始
        /// </summary>
        public int StartIndexData
        {
            get
            {
                return (int)((modelSign & 0xFF0) >> 4);
            }
            set
            {
                modelSign = (modelSign & 0xFF00F) + (value << 4);
            }
        }
        /// <summary>
        /// 卡号或者核号
        /// </summary>
        public int CardIDOrModelID
        {
            get
            {
                return (int)((modelSign & 0xF));
            }
            set
            {
                modelSign = (modelSign & 0xFFFF0) + value;
            }
        }
        #endregion
    }
    /// <summary>
    /// 额外的PID配置
    /// </summary>
    [Serializable]
    public class CPidExt
    {
        /// <summary>
        /// 通过ID号来指定临时PID,每个轴有主的PID
        /// </summary>
        public string id { get; set; } = "";
        /// <summary>
        /// 速度环的闭环,GVN卡专用
        /// </summary>
        public short closeVelLoop { get; set; } = 0;//>0表示闭环
        /// <summary>
        /// 速度环闭环中使用的PID参数
        /// </summary>
        public float velloopkp { get; set; } = 1;
        public float velloopki { get; set; } = 0;
        public float velloopkd { get; set; } = 0;
        public float velloopkvff { get; set; } = 0;
        public float velloopkaff { get; set; } = 0;
        public float velloopkilmt { get; set; } = 32767;
        public float velloopkdlmt { get; set; } = 32767;
        public float vellooplmt { get; set; } = 32767;
        /// <summary>
        /// 速度环加加速度前馈
        /// </summary>
        public float velloopkjff { get; set; } = 0;
        /// <summary>
        /// gvn特有的设置dac的输出
        /// 当使用gshd-gii做全闭环的时候,需要设置成512
        /// 当使用三方伺服或者gtm做的时候,要设置成1
        /// </summary>
        //public long dacscale { get; set; } = 1;
        /// <summary>
        /// 位置环的模式
        /// 闭环控制模式
        /// 0=开环 1=闭环
        /// </summary>
        public short closeLoop { get; set; } = 0;//>0表示闭环
        /// <summary>
        /// 位置环闭环中使用的PID参数
        /// </summary>
        public float kp { get; set; } = 1;
        public float ki { get; set; } = 0;
        public float kd { get; set; } = 0;
        public float kvff { get; set; } = 0;
        public float kaff { get; set; } = 0;
        public float kilmt { get; set; } = 32767;
        public float kdlmt { get; set; } = 32767;
    }
    /// <summary>
    /// 单个轴的参数
    /// </summary>
    [Serializable]
    public class CAxisSinglePrm
    {
        /// <summary>
        /// 默认的构造函数1
        /// 序列化必须要
        /// </summary>
        public CAxisSinglePrm()
        {
            //for (int i = 0; i < 4; i++)
            //{
            //    CPidExt p = new CPidExt();
            //    extpids.Add(p);
            //}
        }
        /// <summary>
        /// 构造函数2
        /// </summary>
        /// <param name="isLine">0=直线 1=旋转</param>
        public CAxisSinglePrm(int isLine)
        {

        }
        private int pididnow = 0;
        /// <summary>
        /// 设置当前PID的ID
        /// 0=使用主的PID
        /// 1-X使用其他的PID
        /// </summary>
        [XmlIgnore]
        public int PidID
        {
            set
            {
                if (value != 0)
                {
                    if (pids.Count==0)//保证多个PID
                    {
                        CPidExt p1 = new CPidExt();
                        pids.Add(p1);
                        CPidExt p2 = new CPidExt();
                        pids.Add(p2);
                    }
                    else if (pids.Count == 1)
                    {
                        CPidExt p3 = new CPidExt();
                        pids.Add(p3);
                    }
                    pids[0] = this.CopyPid();
                    this.RestorePid(value);
                    //先备份
                    pididnow = value;
                }
                else
                {
                    if (pididnow ==0)
                    {
                        //do nothing
                    }
                    else
                    {
                        this.RestorePid(0);
                        pididnow = 0;
                    }
                }
            }
        }
        #region 全局属性
        /// <summary>
        ///  唯一的ID号,其他地方全部用这个ID号来指定某个轴和他的映射关系
        /// </summary>
        public int ID { get; set; } = 0;
        /// <summary>
        /// 轴参数的组号,配合dIndex应用
        /// 相同的组号说明是硬件上一组的
        /// </summary>
        public int groupID { get; set; } = 0;
        /// <summary>
        /// 数据放在哪个区域
        /// 上位机批量读取的时候使用
        /// 这个是自动生成的,就算表格中有修改,也会被自动填充.
        /// </summary>
        public int dIndex { get; set; } = 0;
        /// <summary>
        /// 属于哪个轴模块
        /// 用于批量读取数据的时候使用,按模块读取会减少很多的PCI占用
        /// </summary>
        public string modelName { get; set; } = "";
        /// <summary>
        /// 轴的名称，UI中使用
        /// 会自动刷新到轴列表中
        /// </summary>
        public string name { get; set; } = "轴";
        /// <summary>
        /// 轴的备注,UI中使用
        /// 会自动刷新到轴列表中
        /// </summary>
        public string note { get; set; } = "无备注";
        /// <summary>
        /// 允许同步编码器
        /// </summary>
        [XmlIgnore]
        public bool EnableSynPos { get; set; } = true;
        /// <summary>
        /// 固高特有参数,卡号
        /// </summary>
        public short cardNum { get; set; } = 0;
        /// <summary>
        /// 固高特有参数,轴号
        /// </summary>
        public short axisNum { get; set; } = 0; 
        /// <summary>
        /// 是否激活
        /// </summary>
        public short active { get; set; } = 0;
        public short map_rel_io { get; set; } = -1;
        /// <summary>
        /// 轴的类型，方便根据轴类型自动设置参数和单位.
        /// 0=直线 1= 旋转 3=纯编码器 4=纯虚拟轴
        /// </summary>
        public short type { get; set; } = 0;
        /// <summary>
        /// 最大加速度
        /// </summary>
        public double maxAcc { get; set; } = 1.0;
        /// <summary>
        /// 最大减速度
        /// </summary>
        public double maxDec { get; set; } = 1.0;
        /// <summary>
        /// 最大的Jerk值
        /// </summary>
        public double jerk { get; set; } = 1.0;
        /// <summary>
        /// 轴的单位
        /// 0=以MM为单位,1=以度为单位.
        /// </summary>
        public short unit { get; set; } = 0;
        /// <summary>
        ///最大速度 500ms/s du/s
        /// </summary>
        public double maxVel { get; set; } = 100.0;
        /// <summary>
        /// 分辨率plus，电机转一圈所需要的分辨率脉冲数
        /// 一般电机默认都是10000
        /// </summary>
        public long resolution { get; set; } = 10000;
        //读取到的编码器转换到这边的时候乘以一个系数
        public double encDangliang { get; set; } = 1;
        //读取到的指令转换到这边的时候乘以一个系数
        public double cmdDangliang { get; set; } = 1;
        /// <summary>
        /// 减速比中的电机转的圈数
        /// 和机械转动圈数的相对应
        /// </summary>
        public short motorCircle { get; set; } = 1;
        /// <summary>
        /// 机构转动圈数
        /// 和电机转动圈数对应
        /// </summary>
        public short mechanicalCircle { get; set; } = 1;
        /// <summary>
        /// 丝杆的节距
        /// 以unit为单位
        /// 可能是mm或者度
        /// </summary>
        public double pitch { get; set; } = 10;
        /// <summary>
        /// 方向,此参数和很多参数都相关,特别是HOME和编码器
        /// 0=正常 1=取反
        /// </summary>
        public short direct { get; set; } = 1;
        /// <summary>
        /// 临时使能龙门功能
        /// 这是一个临时变量,不会保存成参数
        /// 起因是自动模式下,如果某两个轴是龙门,直接就按龙门启动了
        /// 但是调试阶段,即使是龙门,也是要先按单个轴启动
        /// 具体用哪种方式,用这个临时变量来决定
        /// >0 表示启用gantrymodel参数 =0 只按照常规的单轴来使能
        /// </summary>
        public short gantryEnable { get; set; } = 0;
        /// <summary>
        /// 0=非龙门模式
        /// 1=常规龙门模式
        /// 2=共轭龙门模式
        /// </summary>
        public short gantryModel { get; set; } = 0;
        /// <summary>
        ///龙门从轴的ID，当初始化的时候需要用这个来自动使能从轴
        /// </summary>
        public short gantrySlaveId { get; set; } = 0;
        /// <summary>
        /// 龙门模式的时候的从轴号
        /// </summary>
        public short gantrySlave { get; set; } = 0;
        /// <summary>
        /// 龙门参数
        /// 2=共轭龙门的时候  gantryPrm[0] = 主轴的配重比利 gantryPrm[1] = 从轴的配重比利
        /// </summary>
        public short[] gantryPrm { get; set; } = new short[6];
        /// <summary>
        /// 是否启用软限位
        /// 1=启用 0=不启用
        /// </summary>
        public short enableSoftlmt { get; set; } = 0;
        /// <summary>
        /// 是否启用报警信号
        /// 1=启用 0=不启用
        /// </summary>
        public short enableAlm { get; set; } = 1;
        /// <summary>
        /// 是否启用正限位
        /// 1=启用 0=不启用
        /// </summary>
        public short enableLmtP { get; set; } = 1;
        /// <summary>
        /// 是否启用负限位
        /// 1=启用 0=不启用
        /// </summary>
        public short enableLmtN { get; set; } = 1;
        /// <summary>
        /// 是否启用跟随误差检测
        /// 1=启用 0=不启用
        /// </summary>
        public short enableFollowErr { get; set; } = 0;
        public double followErrPos { get; set; } = 1.0;
        public double arrivePand { get; set; } = 5.0;
        public double followeTime { get; set; } = 1.0;
        public long arriveDelay { get; set; } = 10;
        /// <summary>
        /// 软限位的值
        /// 单位是unit指定
        /// </summary>
        public double softlmtp { get; set; } = 0;
        public double softlmtn { get; set; } = 0;
        /// <summary>
        /// 启用螺距补偿
        /// </summary>
        public short enablescrew { get; set; } = 0;
        /// <summary>
        /// 补偿表起点
        /// </summary>
        public double screwstartpos { get; set; } = 0;
        /// <summary>
        /// 补偿长度
        /// </summary>
        public double screwlenth { get; set; } = 0;
        /// <summary>
        /// 导入螺距数据
        /// </summary>
        /// <param name="path"></param>
        /// <param name="importtype">0=</param>
        /// <returns></returns>
        public bool ImportScrewData(string path,short importtype = 0)
        {
            try
            {
                StreamReader sr = new StreamReader(path, FileEncoding.GetType(path));
                if (sr == null)
                    return false;
                //
                screwp.Clear();
                screwn.Clear();
                //清空数据
                #region csv文件读取到list中
                List<string> lines = new List<string>();
                try
                {
                    //读取
                    while (!sr.EndOfStream)
                    {
                        string str = sr.ReadLine();
                        lines.Add(str);
                    }
                    sr.Close();
                    double startpos = 0;
                    double lenthdist = 0;
                    //解析
                    for (int i = 0; i < lines.Count; i++)
                    {
                        string[] divstr = lines[i].Split(',');
                        if (divstr.Length < 4)
                        {
                            return false;
                        }
                        if (i == 0)
                            continue;
                        if (i==1)
                        {
                            startpos = double.Parse(divstr[1]) / 1000.0;
                        }
                        if (i== lines.Count-1)
                        {
                            lenthdist = double.Parse(divstr[1]) / 1000.0 - startpos;
                        }
                        double p1 = double.Parse(divstr[2]) / 1000.0;
                        double p2 = double.Parse(divstr[3]) / 1000.0;
                        screwp.Add(p1);
                        screwn.Add(p2);
                    }
                    screwstartpos = startpos;
                    screwlenth = lenthdist;
                    return true;
                }
                catch (Exception)
                {
                    screwp.Clear();
                    screwn.Clear();
                    return false;
                }
                #endregion
            }
            catch (Exception)
            {
                return false;
            }
        }
        /// <summary>
        /// 正向螺距补偿
        /// </summary>
        public List<double> screwp = new List<double>();
        /// <summary>
        /// 负向螺距补偿
        /// </summary>
        public List<double> screwn = new List<double>();
        /// <summary>
        /// 到位类型判断
        /// 0=默认的 指令到达就到达了
        /// 1=指令到达后,延迟arriveDelay的时间后到达
        /// 2=指令和编码器都要到达
        /// 4=指令和编码器都到达后,还需要延迟arriveDelay后才置位.
        /// </summary>
        public short arriveType { get; set; } = 0;
        //---------------交叉补偿--------------------//
        public short enableCross = 0;
        public short crossSourcesAxis = 0;
        public double crossRate = 1900;
        //--------------弹簧力补偿参数----------------
        public short sprinenable { get; set; } = 0;//0=cancel 1=enable
        /// <summary>
        /// index相对于机械原点/回零后的偏移，单位plus 有正负
        /// </summary>
        public short sprineindexoffset { get; set; } = 0;
        /// <summary>
        /// 线性补偿公式中的y=kx+b中的k
        /// </summary>
        public double sprinek { get; set; } = 0;
        /// <summary>
        /// 线性补偿公式中的y=kx+b中的b;
        /// </summary>
        public double sprineb { get; set; } = 0;
        /// <summary>
        /// 弹簧补偿的最大范围，绝对值[0,32767],对应的10v
        /// </summary>
        public double sprinelimit { get; set; } = 0;
        /// <summary>
        /// 额外的PID列表
        /// </summary>
        public List<CPidExt> pids = new List<CPidExt>();
        /// <summary>
        /// 复制PID
        /// </summary>
        /// <returns></returns>
        public CPidExt CopyPid()
        {
            CPidExt rst = new CPidExt();
            rst.closeLoop = this.closeLoop;
            rst.kvff = this.kvff;
            rst.kaff = this.kaff;
            rst.kd = this.kd;
            rst.kp = this.kp;
            rst.ki = this.ki;
            rst.kilmt = this.kilmt;
            rst.kdlmt = this.kdlmt;
            //剫换
            rst.closeVelLoop = this.closeVelLoop;
            rst.velloopkjff = this.velloopkjff;
            rst.velloopkvff = this.velloopkvff;
            rst.velloopkp = this.velloopkp;
            rst.velloopki = this.velloopki;
            rst.velloopkd= this.velloopkd;
            rst.velloopkaff = this.velloopkaff;
            rst.velloopkdlmt= this.velloopkdlmt;
            rst.velloopkilmt = this.velloopkilmt;
            rst.vellooplmt = this.vellooplmt;

            return rst;
    }
        /// <summary>
        /// 把列表中的pid恢复到当前
        /// </summary>
        /// <param name="index"></param>
        public void RestorePid(int index)
        {
            var inpid = pids[index];
            this.closeLoop = inpid.closeLoop;
            this.kvff = inpid.kvff;
            this.kaff = inpid.kaff;
            this.kd = inpid.kd;
            this.kp = inpid.kp;
            this.ki = inpid.ki;
            this.kilmt = inpid.kilmt;
            this.kdlmt = inpid.kdlmt;
            //剫换
            this.closeVelLoop = inpid.closeVelLoop;
            this.velloopkjff = inpid.velloopkjff;
            this.velloopkvff = inpid.velloopkvff;
            this.velloopkp = inpid.velloopkp;
            this.velloopki = inpid.velloopki;
            this.velloopkd = inpid.velloopkd;
            this.velloopkaff = inpid.velloopkaff;
            this.velloopkdlmt = inpid.velloopkdlmt;
            this.velloopkilmt = inpid.velloopkilmt;
            this.vellooplmt = inpid.vellooplmt;
        }
        /// <summary>
        /// 当前使用的PID,使能会改变
        /// 0=主的pid值
        /// </summary>
        public short pidIndex { get;set; } = 0;
        /// <summary>
        /// 速度环的闭环,GVN卡专用
        /// </summary>
        public short closeVelLoop { get; set; } = 0;//>0表示闭环
        /// <summary>
        /// 速度环闭环中使用的PID参数
        /// </summary>
        public float velloopkp { get; set; } = 1;
        public float velloopki { get; set; } = 0;
        public float velloopkd { get; set; } = 0;
        public float velloopkvff { get; set; } = 0;
        public float velloopkaff { get; set; } = 0;
        public float velloopkilmt { get; set; } = 32767;
        public float velloopkdlmt { get; set; } = 32767;
        public float vellooplmt { get; set; } = 32767;
        /// <summary>
        /// gvn特有的设置dac的输出
        /// 当使用gshd-gii做全闭环的时候,需要设置成512
        /// 当使用三方伺服或者gtm做的时候,要设置成1
        /// </summary>
        public long dacscale{ get; set; } = 1;
        /// <summary>
        /// 位置环的模式
        /// 闭环控制模式
        /// 0=开环 1=闭环
        /// </summary>
        public short closeLoop { get; set; } = 0;//>0表示闭环
        /// <summary>
        /// 位置环闭环中使用的PID参数
        /// </summary>
        public float kp { get; set; } = 1;
        public float ki { get; set; } = 0;
        public float kd { get; set; } = 0;
        public float kvff { get; set; } = 0;
        /// <summary>
        /// 速度环加加速度前馈
        /// </summary>
        public float velloopkjff { get; set; } = 0;
        public float kaff { get; set; } = 0;
        public float kilmt { get; set; } = 32767;
        public float kdlmt { get; set; } = 32767;
        /// <summary>
        /// 绝对值编码器的时候,圈数
        /// </summary>
        public double absCircle { get; set; } = 0;
        /// <summary>
        /// 绝对值编码器的每一圈的编码器数
        /// </summary>
        public double absEncSingle { get; set; } = 0;//绝对值的时候用
        public double absEncPosByRead { get; set; } = 0;//当前读取到的绝对值编码器的值
        public double absEncZeroPos { get; set; } = 0;//绝对值编码器的0的位置 记录
        public double absEnc2AddRate { get; set; } = 1;//绝对值的差值算出来后转换到增量位置的比例
        public short absEncDirect { get; set; } = 1;//1 相同 -1 相反绝对值方向和增量方向相同最后计算的时候差值直接算
        /// <summary>
        /// 原点和正负限位开关取反
        /// </summary>
        public short reverseHome { get; set; } = 0;
        public short reverseLmtp { get; set; } = 0;
        public short reverseLmtn { get; set; } = 0;
        /// <summary>
        /// 回零的方式,请参考COE402协议
        /// -1 表示当前位置清零 
        /// 400=gvn的碰撞回零+index,碰撞判断跟随误差
        /// 
        /// >=500表示自定义回零,用捕获方式来回零
        /// 
        /// >=1000表示绝对值方式 1000=rs232 1001=tcp
        /// </summary>
        public short homeType { get; set; } = 0;
        /// <summary>
        /// 绝对值编码器的时候
        /// 需要用到哪家驱动器
        /// </summary>
        public string driverTypeName { get; set; } = "";
        /// <summary>
        /// 绝对值编码器的参数定义
        /// 不同驱动定义不同
        /// </summary>
        public string driverPrmString { get; set; } = "COM1";
        /// <summary>
        /// 回零的搜索距离
        /// 1=正方向无线搜索
        /// -1=负方向无线索索
        /// 其余的值代表搜索距离
        /// </summary>
        public double homeSearch { get; set; } = -1000.0f;
        /// <summary>
        /// 回零顺序
        /// 按照0-x的顺序,会自动完成回原点操作。
        /// </summary>
        public short homeSequence { get; set; } = 0;
        /// <summary>
        /// 碰到正负限位或者Home开关的时候,需要脱离开多远距离后才能重新开始回
        /// </summary>
        public double homeLeave { get; set; } = 100;
        public short homeTriger { get; set; } = 0;//GSN 1=上升沿 0=下降沿
        /// <summary>
        /// 回零后的偏移,这个是要实际移动的
        /// </summary>
        public double homeoffset { get; set; } = 0;
        /// <summary>
        /// 回零后的偏移,这个是回零完成后,把这个位置设置成某个值
        /// </summary>
        public double homeoffsetSet { get; set; } = 0;
        /// <summary>
        /// 回零后延迟多少时间清零
        /// </summary>
        public double homeZeroDelay { get; set; } = 100;
        /// <summary>
        /// 回零完成后,需要增量移动到多少,记住这个是增量
        /// </summary>
        public double homeOverMoveAddtion { get; set; } = 0;
        public double homeVel1 { get; set; } = 50.0f;
        public double homeVel2 { get; set; } = 25.0f;            //回零相关
        /// <summary>
        /// 反向间隙
        /// </summary>
        public double backLash { get; set; } = 0;
        /// <summary>
        /// 急停减速度
        /// </summary>
        public double estopdec { get; set; } = 9800.0f;
        /// <summary>
        /// 平滑时间
        /// </summary>
        public double smoothTime { get; set; } = 10;
        #endregion
        #region 私有成员
        private CAxisRef usedPointer = null;//这个参数被某个模块使用了 就会映射到这里,方便其他UI调用
        private IAbsEncBase absRd = null;//如果是绝对值的 用来读取绝对值 使用前需要赋值
        #endregion
        #region 接口属性
        [XmlIgnore]
        public CAxisRef AxisRefPointer
        {
            get
            {
                return usedPointer;
            }
            set
            {
                usedPointer = value;
            }
        }
        [XmlIgnore]
        public IAbsEncBase AbsReader
        {
            get
            {
                return absRd;
            }
            set
            {
                absRd = value;
            }
        }
        #endregion
        #region 函数
        //急停中的急停方式减速度
        /// <summary>
        /// 获取脉冲/MS2的单位
        /// </summary>
        /// <returns></returns>
        public double getAccPlsPerMs2()
        {
            return maxAcc * 9.8 / pitch * resolution / 1000.0;
        }
        /// <summary>
        /// 从加速度G转换成plus/ms2
        /// </summary>
        /// <param name="g"></param>
        /// <returns></returns>
        public double AccG2PlusPerMs2(double g)
        {
            return g * 9.8 * 1000.0 / pitch * resolution / 1000000.0;
        }
        /// <summary>
        /// 从加速度G转换成plus/ms2
        /// </summary>
        /// <param name="g"></param>
        /// <returns></returns>
        public double PlusPerMs2_AccG(double value)
        {
            return value / resolution * pitch / 1000.0 * 1000000.0/9.8;
        }
        //急停中的急停方式减速度
        /// <summary>
        /// 获取脉冲/MS2的单位
        /// </summary>
        /// <returns></returns>
        public double getDecPlsPerMs2()
        {
            return maxDec * 9.8 / pitch * resolution / 1000.0;
        }
        /// <summary>
        /// 获取P/MS
        /// </summary>
        /// <returns></returns>
        public double getVelPlsPerMs()
        {
            return maxVel / pitch * resolution / 1000.0;
        }
        /// <summary>
        /// MM转换成脉冲
        /// </summary>
        /// <param name="mm"></param>
        /// <returns></returns>
        public double mm2pls(double mm)
        {
            return mm / pitch * resolution;
        }
        public double pls2mm(double pls)
        {
            return pls * pitch / resolution;
        }
        public double mmpers2plsperms(double vel)
        {
            return vel / pitch * resolution / 1000.0;
        }
        public double plsperms2mmpers(double vel)
        {
            return vel * pitch / resolution * 1000.0;
        }
        public double mmpers22plsperms2(double acc)
        {
            return acc / pitch * resolution / 1000.0;
        }
        #endregion
    }
    /// <summary>
    /// 基于CSV文件的IO配置,源文件用手动编辑
    /// 好处是可以用excel方便快捷的编辑
    /// 用于统一所有IO的映射和读取关系
    /// </summary>
    [Serializable]
    public class CIoConfige
    {
        /// <summary>
        /// 按模块来保存所有的IO点.
        /// </summary>
        public List<CIoModel> data = new List<CIoModel>();//IO模块列表
        /// <summary>
        /// 单独数据都保存在这里.同时会被上面的轴模块引用保存
        /// </summary>
        public List<CIoSinglePrm> prmList = new List<CIoSinglePrm>();//所有单个IO点的配置
        public CIoConfige()
        {
        }
        public void addModel(ref CIoModel md)
        {
            data.Add(md);
        }
        public void addIoSinglePrm(ref CIoSinglePrm p)
        {
            prmList.Add(p);
        }
        /// <summary>
        /// 设置IO参数的index
        /// 把模块的卡号轴号模块号等 都传递到单个IO中
        /// 方便单独操作某一个IO变量的时候会用
        /// </summary>
        /// <param name="md"></param>
        /// <param name="p"></param>
        /// <param name="tp"></param>
        private void setsingleIoIndex(CIoModel md, ref CIoSinglePrm p,ESingleIoType tp)
        {
            switch (tp)
            {
                case ESingleIoType.Input:
                    p.dIndex = ++CIoModel.CounterDi;
                    p.startIndex = (short)(md.iList.Count + md.prm.DiStartIndex);
                    p.CardIDOrModelID = (short)md.prm.CardIDOrModelID;
                    p.IsLocal = md.prm.IsLocal;
                    p.ExtModelID = (short)md.prm.ExtModelID;
                    break;
                case ESingleIoType.Output:
                    p.dIndex = ++CIoModel.CounterDo;
                    p.startIndex = (short)(md.oList.Count + md.prm.DoStartIndex); 
                    p.CardIDOrModelID = (short)md.prm.CardIDOrModelID;
                    p.IsLocal = md.prm.IsLocal;
                    p.ExtModelID = (short)md.prm.ExtModelID;
                    break;
                case ESingleIoType.AInput:
                    p.dIndex = ++CIoModel.CounterAi;
                    p.startIndex = (short)(md.aiList.Count + md.prm.AiStartIndex);
                    p.CardIDOrModelID = (short)md.prm.CardIDOrModelID;
                    p.IsLocal = md.prm.IsLocal;
                    p.ExtModelID = (short)md.prm.ExtModelID;
                    break;
                case ESingleIoType.AOutput:
                    p.dIndex = ++CIoModel.CounterAo;
                    p.startIndex = (short)(md.aoList.Count + md.prm.AoStartIndex);
                    p.CardIDOrModelID = (short)md.prm.CardIDOrModelID;
                    p.IsLocal = md.prm.IsLocal;
                    p.ExtModelID = (short)md.prm.ExtModelID;
                    break;
                case ESingleIoType.EncoderInut:
                    p.dIndex = ++CIoModel.CounterEnc;
                    p.startIndex = (short)(md.encList.Count + md.prm.EncStartIndex);
                    p.CardIDOrModelID = (short)md.prm.CardIDOrModelID;
                    p.IsLocal = md.prm.IsLocal;
                    p.ExtModelID = (short)md.prm.ExtModelID;
                    break;
                case ESingleIoType.CompareOut:
                    p.dIndex = ++CIoModel.CounterCmpout;
                    p.startIndex = (short)(md.cmpList.Count + md.prm.CmpStartIndex);
                    p.CardIDOrModelID = (short)md.prm.CardIDOrModelID;
                    p.IsLocal = md.prm.IsLocal;
                    p.ExtModelID = (short)md.prm.ExtModelID;
                    break;
            }
        }
        /// <summary>
        /// 通过字符串来设置当前IO的类型
        /// </summary>
        /// <param name="str"></param>
        /// <param name="tp"></param>
        /// <returns></returns>
        private bool getsingleIoType(string str,ref ESingleIoType tp)
        {
            if (str.IndexOf("输入") >= 0)
            {
                tp = ESingleIoType.Input;
                return true;
            }
            else if (str.IndexOf("输出") >= 0)
            {
                tp = ESingleIoType.Output;
                return true;
            }
            else if (str.IndexOf("模拟量输入") >= 0)
            {
                tp = ESingleIoType.AInput;
                return true;
            }
            else if (str.IndexOf("模拟量输出") >= 0)
            {
                tp = ESingleIoType.AOutput;
                return true;
            }
            else if (str.IndexOf("编码器") >= 0)
            {
                tp = ESingleIoType.EncoderInut;
                return true;
            }
            else if (str.IndexOf("比较输出") >= 0)
            {
                tp = ESingleIoType.CompareOut;
                return true;
            }
            else
            {
                tp = ESingleIoType.Input;
                return false;
            }
        }
        /// <summary>
        /// 系统默认不适用这个导入
        /// 这个功能适用于在UI参数界面导入
        /// 读取IO的模块配置文件
        /// 这个IO配置是从硬件自动导出的
        /// 然后根据用户修改的.
        /// </summary>
        /// <param name="csv">文件名和路径</param>
        /// <returns></returns>
        public bool readfromcsv(string csv = @"confige/io.csv")
        {
            try
            {
                StreamReader sr = new StreamReader(csv, FileEncoding.GetType(csv));
                if (sr == null)
                    return false;
                //清空数据
                #region csv文件读取到list中
                List<string> lines = new List<string>();
                try
                {
                    while (!sr.EndOfStream)
                    {
                        string str = sr.ReadLine();
                        lines.Add(str);
                    }
                    sr.Close();
                }
                catch (Exception)
                {
                    return false;
                }
                #endregion
                //解析文件--第一行是假数据
                CIoModel.clear();
                data.Clear();
                CIoModel tmoModel = null;
                for (int i = 1; i < lines.Count; i++)
                {
                    string[] line = lines[i].Split(',');
                    //创建一个模块
                    if (line[0] != "")
                    {
                        if (tmoModel != null)
                            data.Add(tmoModel);
                        tmoModel = new CIoModel();
                        //数据
                        tmoModel.name = line[0];
                        if (line[1] != "")
                            tmoModel.prm.modelSign = long.Parse(line[1]);
                        if (line[2] != "")
                            tmoModel.prm.startIndexSign = long.Parse(line[2]);
                    }
                    //放入数据
                    CIoSinglePrm tmp = new CIoSinglePrm();
                    tmp.id = short.Parse(line[3]);
                    string mdType = line[4];
                    ESingleIoType tmpType = ESingleIoType.Input;
                    if(getsingleIoType(mdType,ref tmpType))
                    {
                        tmp.ioType = tmpType;
                        setsingleIoIndex(tmoModel, ref tmp, tmpType);
                    }
                    else
                        return false;
                    tmp.pinName = line[5];
                    tmp.name = line[6];
                    if (line[7] == "正常")
                        tmp.reverse = false;
                    else
                        tmp.reverse = true;
                    tmoModel.addIO(tmp);
                    prmList.Add(tmp);
                }
                //最后一次推出的时候 还有个模块没添加,在这里添加
                if (tmoModel != null)
                {
                    data.Add(tmoModel);
                    tmoModel = null;
                }
                return true;
            }
            catch (Exception)
            {
                System.Windows.Forms.MessageBox.Show("IO表读取异常!");
                return false;
            }
        }
    }
    /// <summary>
    /// 模块本身的参数
    /// </summary>
    public class CIoModelPrm
    {
        //固高卡特有
        //对于GSN来的轴模块来说AAA_XXXX_YYYY_ZZZZ
        //GSN-ZZZZ是扩展模块的ID站号 0-15 
        //GSN-YYYY是本地还是扩展=0是本地 >0是扩展
        //GSN-XXXX是核心号或者卡号
        //GSN-AAAA是模块的ID号 从0开始
        public long modelSign = 0; 
        /// <summary>
        /// 每个值得起始Index
        /// 格式:6666_5555_4444_3333_22222222_11111111
        /// 说明:1=输入的 2=输出的 3= 模拟量输入 4= 模拟量输出 5=编码器输入 6=位置比较输出
        /// </summary>
        public long startIndexSign = 0;
        //存放数据开始位置-这个数据实在初始化模块参数的时候自动恒成的.
        public short diDataStart = 0;
        public short doDataStart = 0;
        public short aiDataStart = 0;
        public short aoDataStart = 0;
        public short encDataStart = 0;
        public short cmpDataStart = 0;
        public void clear()
        {
            modelSign = 0;
            startIndexSign = 0;
            diDataStart = 0;
            doDataStart = 0;
            aiDataStart = 0;
            aoDataStart = 0;
            encDataStart = 0;
            cmpDataStart = 0;
        }
        #region 属性
        public short EncStartIndex
        {
            get
            {
                return (short)((startIndexSign & 0xF000000) >> 24);
            }
            set
            {
                startIndexSign = (startIndexSign & 0xF0FFFFFF) + (value << 24);
            }
        }
        public short AoStartIndex
        {
            get
            {
                return (short)((startIndexSign & 0xF00000) >> 20);
            }
            set
            {
                startIndexSign = (startIndexSign & 0xFF0FFFFF) + (value << 20);
            }
        }
        public short AiStartIndex
        {
            get
            {
                return (short)((startIndexSign & 0xF0000) >> 16);
            }
            set
            {
                startIndexSign = (startIndexSign & 0xFFF0FFFF) + (value << 16);
            }
        }
        public short DoStartIndex
        {
            get
            {
                return (short)((startIndexSign & 0xFF00) >> 8);
            }
            set
            {
                startIndexSign = (startIndexSign & 0xFFFF00FF) + (value << 8);
            }
        }
        public short DiStartIndex
        {
            get
            {
                return (short)(startIndexSign & 0xFF);
            }
            set
            {
                startIndexSign = (startIndexSign & 0xFFFFFF00) + value;
            }
        }
        public short CmpStartIndex
        {           
            get
            {
                return (short)(startIndexSign & 0xF0000000);
            }
            set
            {
                startIndexSign = (startIndexSign & 0x0FFFFFFF) + value;
            }
        }
        public int ModelID
        {
            get
            {
                return (int)((modelSign & 0xF000) >> 8);
            }
            set
            {
                modelSign = (modelSign & 0x0FFF) + (value << 12);
            }
        }
        /// <summary>
        /// 卡号或者核号
        /// </summary>
        public int CardIDOrModelID
        {
            get
            {
                return (int)((modelSign & 0xF00) >> 8);
            }
            set
            {
                modelSign = (modelSign & 0xF0FF) + (value << 8);
            }
        }
        /// <summary>
        /// 是否是本地模块
        /// </summary>
        public bool IsLocal
        {
            get
            {
                return ((modelSign & 0xF0) >> 4) == 0;
            }
            set
            {
                if (value)
                {
                    modelSign = (modelSign & 0xF0F);
                }
                else
                {
                    modelSign = (modelSign & 0xFF0F) + (1 << 4);
                }
            }
        }
        /// <summary>
        /// 如果是扩展模块 这里表示扩展模块的站号0-15
        /// </summary>
        public int ExtModelID
        {
            get
            {
                return (int)(modelSign & 0xF);
            }
            set
            {
                modelSign = (modelSign & 0xFFF0) + value;
            }
        }
        #endregion
    }
    
    /// <summary>
    /// 坐标系管理
    /// </summary>
    [Serializable]
    public class CCrdPrm
    {
        public static string defaultfn = @"confige/crd.xml";
        public List<CCrdPrmSingle> data = new List<CCrdPrmSingle>();
        /// <summary>
        /// 构造函数读取数据
        /// </summary>
        public CCrdPrm()
        {

        }
        /// <summary>
        /// 从csv格式的文件中读取配置
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public bool readfromcsv(string csv = @"confige/crd.csv")
        {
            if (csv == "")
                return false;
            return true;
        }
        public bool save2xml(string xml = @"confige/crd.xml")
        {
            return CXmlSerializerManager.save(this, xml);
        }
        /// <summary>
        /// 通过ID来获取一个坐标系参数
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public CCrdPrmSingle get(string id)
        {
            foreach (var item in data)
            {
                if (id == item.ID)
                    return item;
            }
            return null;
        }
    }
    /// <summary>
    /// 坐标系的参数
    /// </summary>
    public class CCrdPrmSingle
    {
        public string ID = "默认坐标系ID";//唯一识别码
        public double maxvel=100, maxacc=1;//mm/s mm/s2
        public int startSeg = 100;//放入多少段后 就需要启动
        public int crdNum = 1;//坐标系号
        public int core=1;//GSN和gen需要有核号
        public int evenTime = 1;
        public int setCrdAsZeroPos = 0;//==0不设置坐标系原点 相当于绝对值  >0 当前位置设置成0
        public int[] axis = new int[6];//每个轴对应的电机 制定XYZABC对应哪个轴 如果要用跟随  轴和当量都要设置,但是不一定要设置几维
        public double[] resolution = new double[6];//每个轴的分辨率 1unit=x个脉冲 比如1000 转换的时候就是*1000

        public int dimension = 3;//几维坐标
        public CCrdPrmSingle()
        {
            for (int i = 0; i < 6; i++)
            {
                resolution[i] = 10000;
            }
            axis[0] = 1;
            axis[1] = 2;
            axis[2] = 3;
        }
        #region 属性
        /// <summary>
        /// 返回维度
        /// </summary>
        public int Dimension
        {
            get
            {
                return dimension;
            }
        }
        #endregion
        /// <summary>
        /// 检测坐标系参数是否OK
        /// </summary>
        public bool ParameterOk
        {
            get
            {
                if (dimension <= 0 && dimension > 5)
                    return false;
                return true;
            }
        }
        /// <summary>
        /// 转换坐标系速度
        /// </summary>
        /// <param name="unit">=0代表获取设定的最大速度 !=0 代表用这个值来转换</param>
        /// <returns></returns>
        public double tranCrdVel(double unit=0)
        {
            //求最小分辨率
            double r = 9000000;
            for (int i = 0; i < 3; i++)
            {
                if (resolution[i] != 0 && resolution[i] < r)
                    r = resolution[i];
            }
            if (unit == 0)
            {
                return maxvel * r / 1000.0;
            }
            else
            {
                return unit * r / 1000.0;
            }
        }
        /// <summary>
        /// 转换坐标系加速度
        /// </summary>
        /// <param name="unit">=0的时候 就是用坐标系的最大速度 >0的时候就用unit来转换</param>
        /// <returns></returns>
        public double tranCrdAcc(double unit=0)
        {
            //求最小分辨率
            double r = 9000000;
            for (int i = 0; i < 3; i++)
            {
                if (resolution[i] != 0 && resolution[i] < r)
                    r = resolution[i];
            }
            if (unit == 0)
            {
                return maxacc * r / 1000000.0;
            }
            else
            {
                return unit * r / 1000000.0;
            }
        }
        /// <summary>
        /// 转换位置
        /// </summary>
        /// <returns></returns>
        public long tranPos(string axis,double pos)
        {
            switch (axis)
            {
                case "x":
                case "X":
                    return (long)(pos * resolution[0]);
                case "Y":
                case "y":
                    return (long)(pos * resolution[1]);
                case "Z":
                case "z":
                    return (long)(pos * resolution[2]);
                case "A":
                case "a":
                    return (long)(pos * resolution[3]);
                case "B":
                case "b":
                    return (long)(pos * resolution[4]);
                case "C":
                case "c":
                    return (long)(pos * resolution[5]);
            }
            return 0;
        }
        public CCrdPrmSingle(int dimension)
        {
            this.dimension = dimension;
            switch (dimension)
            {
                case 1:
                    break;
                case 2:
                    break;
                case 3:
                    break;
                case 4:
                    break;
                default:
                    break;
            }
        }
        public void setID(string id)
        {
            ID = id;
        }
        public object clone()
        {
            CCrdPrmSingle p = new CCrdPrmSingle();
            p.ID = this.ID;
            p.dimension = this.dimension;
            p.maxvel = this.maxvel;
            p.maxacc = this.maxacc;
            p.core = this.core;
            p.crdNum = this.crdNum;
            p.setCrdAsZeroPos = this.setCrdAsZeroPos;
            p.evenTime = this.evenTime;
            for (int i = 0; i < 6; i++)
            {
                p.axis[i] = this.axis[i];
                p.resolution[i] = this.resolution[i];
            }
            return p;
        }
    }
    /// <summary>
    /// 电子凸轮的配置参数
    /// </summary>
    [Serializable]
    public class CCamPrm
    {
        public static string defaultfn = @"confige/cam.xml";
        public List<CSingleCamPrm> data = new List<CSingleCamPrm>();
        public CCamPrm()
        {
            //var p = (CCamPrm)CXmlSaveRead.read(@"confige\cam.xml", typeof(CCrdPrm));
            //if (p != null)
            //{
            //    data = p.data;
            //}
        }
        /// <summary>
        /// 从csv格式的文件中读取配置
        /// </summary>
        /// <param name="csv"></param>
        /// <returns></returns>
        public bool readfromcsv(string csv = @"confige/cam.csv")
        {
            if (csv == "")
                return false;
            return true;
        }
        /// <summary>
        /// 通过ID来获取一个坐标系参数
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public CSingleCamPrm get(string id)
        {
            foreach (var item in data)
            {
                if (id == item.ID)
                    return item;
            }
            return null;
        }
    }
    /// <summary>
    /// 单个凸轮数据类
    /// </summary>
    public class CSingleCamPrm
    {
        public string ID = "";//空表示无效
    }
    
    /// <summary>
    /// 单个输入输出的参数
    /// 通过这个参数,可以单独读取,设置某一个IO点.
    /// </summary>
    public class CIoSinglePrm
    {
        //固高卡特有
        //对于GSN来的轴模块来说XXXX_YYYY_ZZZZ
        //GSN-ZZZZ是扩展模块的ID站号 0-15 
        //GSN-YYYY是本地还是扩展=0是本地 >0是扩展
        //GSN-XXXX是核心号或者卡号
        public long modelType = 0;
        //固高特有参数
        //public short cardNum = 0;       //卡号或者核号
        //public short extId = -1;        //-1就是本地的,0-X属于扩展模块
        //通用参数 读写用
        public short id = 0;            //0 表示没有映射,读写都无效 唯一的ID号,用来做映射的.
        public short startIndex = 0;    //-自动填充一般一个模块读出来都是一个int或者long 这个输入在模块中的位子和
        public bool reverse = false;    //取反 会影响上层的值
        public bool canOuput = true;    //用在输出,有的输出是不允许被控制的。
        public ESingleIoType ioType = ESingleIoType.Input;
        public short bit = 16;          //位数 模拟量的时候使用
        public short dIndex = 0;        //数据区的地址-自动填充 从1开始的 0当备用
        //界面中显示
        public string name = "";//名称    
        public string modelName = "";//所属的模块的名称
        public string pinName = "";//所属的针脚
        public string note = "";//备注说明
        #region 属性
        /// <summary>
        /// 返回当前IO点的类型字符串
        /// </summary>
        public string IoTypeStr
        {
            get
            {
                switch (ioType)
                {
                    case ESingleIoType.Input:
                        return "输入";
                    case ESingleIoType.Output:
                        return "输出";
                    case ESingleIoType.AInput:
                        return "模拟输入";
                    case ESingleIoType.AOutput:
                        return "模拟输出";
                    case ESingleIoType.EncoderInut:
                        return "编码器输入";
                    case ESingleIoType.CompareOut:
                        return "比较输出";
                    default:
                        return "";
                }
            }
        }
        /// <summary>
        /// 卡号或者核号
        /// </summary>
        public short CardIDOrModelID
        {
            get
            {
                return (short)((modelType & 0B111100000000) >> 8);
            }
            set
            {
                modelType = (short)((modelType & 0B000011111111) + (value << 8));
            }
        }
        /// <summary>
        /// 是否是本地模块
        /// </summary>
        public bool IsLocal
        {
            get
            {
                return ((modelType & 0B11110000) >> 4) != 0;
            }
            set
            {
                if (value)
                {
                    modelType = (short)((modelType & 0B111100001111) + (1 << 4));
                }
                else
                {
                    modelType = (short)(modelType & 0B111100001111);
                }
            }
        }
        /// <summary>
        /// 如果是扩展模块 这里表示扩展模块的站号0-15
        /// </summary>
        public int ExtModelID
        {
            get
            {
                return (int)(modelType & 0B1111);
            }
            set
            {
                modelType = (short)((modelType & 0B111111110000) + value);
            }
        }
        #endregion
    }
    
}
